package com.ihr360.payroll.service.salaryplan;

import com.ihr360.authority.sharing.annonation.AccessControl;
import com.ihr360.authority.sharing.privilege.DeleteOnePrivilege;
import com.ihr360.commons.context.session.Ihr360SessionContextHolder;
import com.ihr360.commons.exception.Ihr360Exception;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.model.type.FieldSource;
import com.ihr360.commons.request.Ihr360SearchBody;
import com.ihr360.event.annonation.RestEventHandler;
import com.ihr360.payroll.dao.PayslipConfigRepository;
import com.ihr360.payroll.dao.SalaryPlanRepository;
import com.ihr360.payroll.dao.SalaryPlanStaffRepository;
import com.ihr360.payroll.model.dto.attendance.plan.AttendancePlan;
import com.ihr360.payroll.model.dto.salary.meta.PayrollConfig;
import com.ihr360.payroll.model.dto.salary.meta.SalaryField;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlan;
import com.ihr360.payroll.model.dto.salary.plan.config.PayslipConfig;
import com.ihr360.payroll.model.vo.PayrollConfigVo;
import com.ihr360.payroll.model.vo.SalaryFieldVo;
import com.ihr360.payroll.service.attendanceplan.AttendancePlanService;
import com.ihr360.payroll.service.eventhandler.SalaryPlanRestEventListener;
import com.ihr360.payroll.service.payroll.PayrollConfigService;
import com.ihr360.payroll.service.salaryprofile.CompanySalaryProfileService;
import com.ihr360.payroll.service.salarytask.SalaryTaskService;
import com.ihr360.payroll.util.SalaryItemSystemNameUtils;
import com.ihr360.rest.reposity.SimpleRestReposity;
import com.ihr360.rest.service.AbstractSimpleRestServiceImpl;
import com.ihr360.validation.ValidationUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author david.wei
 */
@Service
@RestEventHandler(SalaryPlanRestEventListener.class)
@AccessControl
public class SalaryPlanServiceImpl extends AbstractSimpleRestServiceImpl<SalaryPlan, Long> implements SalaryPlanService {

    @Autowired
    SalaryPlanRepository salaryPlanRepository;
    @Autowired
    CompanySalaryProfileService companySalaryProfileService;
    @Autowired
    PayslipConfigRepository payslipConfigRepository;
    @Autowired
    SalaryTaskService salaryTaskService;
    @Autowired
    SalaryPlanStaffRepository salaryPlanStaffRepository;
    @Autowired
    PayrollConfigService payrollConfigService;
    @Autowired
    AttendancePlanService attendancePlanService;


    @Override
    public SimpleRestReposity<SalaryPlan, Long> getSimpleRestReposity() {
        return salaryPlanRepository;
    }

    @Override
    public boolean addStaffsToPlan(List<String> staffIds, Long planId) {
        throw new Ihr360Exception(null, "Not implement yet");
    }

    @Override
    public boolean removeStaffsFromPlan(List<String> staffIds, Long planId) {
        throw new Ihr360Exception(null, "Not implement yet");
    }

    @Override
    public List<String> getStaffsInPlan(Long planId) {
        throw new Ihr360Exception(null, "Not implement yet");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayslipConfig savePayslipConfig(Long planId, PayslipConfig payslipConfig) {
        SalaryPlan salaryPlan = super.getOne(planId);
        payslipConfigRepository.save(payslipConfig);
        salaryPlan.getPayslipConfigs().add(payslipConfig);
        salaryPlanRepository.save(salaryPlan);
        return payslipConfig;
    }

    @Override
    public List<SalaryFieldVo> getPlanSalaryFieldsWithOutAuth(Long planId) {
        SalaryPlan salaryPlan = salaryPlanRepository.findOne(planId);
        ValidationUtils.rejectIfEmpty(salaryPlan, null, "方案不存在，请刷新后重试");
        List<SalaryField> processedSalaryFields = payrollConfigService.getProcessedSalaryFieldsWithOutAuth(salaryPlan.getCompanyId(), salaryPlan.getPayrollConfig().getId());
        if (CollectionUtils.isEmpty(processedSalaryFields)) {
            return Collections.emptyList();
        }
        return getFieldVos(processedSalaryFields);
    }

    @Override
    public List<SalaryFieldVo> getPlanSalaryFields(Long planId) {
        SalaryPlan salaryPlan = super.getOne(planId);
        return getSalaryFields(salaryPlan);
    }

    private List<SalaryFieldVo> getSalaryFields(SalaryPlan salaryPlan) {
        ValidationUtils.rejectIfEmpty(salaryPlan, null, "方案不存在，请刷新后重试");
        List<SalaryField> processedSalaryFields = payrollConfigService.getProcessedSalaryFields(salaryPlan.getCompanyId(), salaryPlan.getPayrollConfig().getId());
        if (CollectionUtils.isEmpty(processedSalaryFields)) {
            return Collections.emptyList();
        }
        return getFieldVos(processedSalaryFields);
    }

    private List<SalaryFieldVo> getFieldVos(List<SalaryField> fields) {
        List<SalaryFieldVo> salaryFieldVoList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(fields)) {
            fields.forEach(field -> {
                PayrollConfigVo payrollConfig = null;
                if (field.getConfig() != null) {
                    PayrollConfig config = field.getConfig();
                    payrollConfig = new PayrollConfigVo(config.getId(), config.getName(), new ArrayList<>(),null);
                }
                salaryFieldVoList.add(new SalaryFieldVo(field.getId(), field.getCode(), payrollConfig, field.getMeta(), field.getSource(),
                        field.getValueMeta(), field.getName(), field.getType(), field.getTaxType(), field.isTaxMergeField(), field.getPersonalSalaryNumOp(),
                        field.getCompanyCostNumOp()));
            });
        }
        return salaryFieldVoList;
    }

    @Override
    public List<SalaryFieldVo> getPlanInputSalaryFields(Long planId) {
        List<SalaryFieldVo> planValidSalaryFields = getPlanSalaryFields(planId);
        planValidSalaryFields.removeIf(field -> FieldSource.INPUT != field.getSource());
        return planValidSalaryFields;
    }

    @Override
    public boolean isPayPeriod(String companyId, Long salaryPlanId) {
        List<SalaryPlan> salaryPlanIdList = salaryTaskService.getActivePlanIds(companyId, salaryPlanId);
        return !CollectionUtils.isEmpty(salaryPlanIdList);
    }

    @Override
    public boolean deleteSystemTemplateById(Long id) {
        SalaryPlan salaryPlan = salaryPlanRepository.findOne(id);
        if (!salaryPlan.isSystemTemplate()) {
            throw new Ihr360Exception("", "您没有权限操作");
        }
        if (isPayPeriod(Ihr360SessionContextHolder.getCompanyId(), id)) {
            ValidationUtils.reject("", "帐期内不允许删除！");
        }
        List<Long> payslipConfigIds = salaryPlan.getPayslipConfigs().stream().map(PayslipConfig::getId).collect(Collectors.toList());

        // 删除员工
        salaryPlanStaffRepository.deleteBySalaryPlanId(id);
        salaryPlan.setPayslipConfigs(new ArrayList<>());
        salaryPlanRepository.save(salaryPlan);
        //　删除工资单
        salaryPlanRepository.delete(id);

        if (!CollectionUtils.isEmpty(payslipConfigIds)) {
            payslipConfigIds.forEach(payslipConfigId -> payslipConfigRepository.delete(payslipConfigId));
        }
        return true;
    }

    @Override
    public boolean deleteById(Long id) {
        SalaryPlan salaryPlan = super.getOne(id);
        if (salaryPlan.isSystemTemplate()) {
            throw new Ihr360Exception("", "您没有权限操作");
        }
        new DeleteOnePrivilege(id, authUserResourceService, iAccessControlClass).doValidate();
        if (isPayPeriod(Ihr360SessionContextHolder.getCompanyId(), id)) {
            ValidationUtils.reject("", "帐期内不允许删除！");
        }
        List<Long> payslipConfigIds = salaryPlan.getPayslipConfigs().stream().map(PayslipConfig::getId).collect(Collectors.toList());
        // 删除员工
        salaryPlanStaffRepository.deleteBySalaryPlanId(id);
        salaryPlan.setPayslipConfigs(new ArrayList<>());
        salaryPlanRepository.save(salaryPlan);
        //　删除工资单
        super.deleteOne(id);
        if (!CollectionUtils.isEmpty(payslipConfigIds)) {
            payslipConfigIds.forEach(payslipConfigId -> payslipConfigRepository.delete(payslipConfigId));
        }
        return true;
    }

    @Override
    public PayslipConfig getDefaultPayslipConfig(Long salaryPlanId) {
        SalaryPlan salaryPlan = super.getOne(salaryPlanId);
        if (salaryPlan != null) {
            for (PayslipConfig payslipConfig : salaryPlan.getPayslipConfigs()) {
                if (payslipConfig.isDefaultPayslipConfig()) {
                    return payslipConfig;
                }
            }
        }
        return null;
    }

    @Override
    public SalaryPlan save(SalaryPlan salaryPlan) {
        SalaryPlan ret = new SalaryPlan();
        if (salaryPlan.getId() != null) {
            ret = salaryPlanRepository.findOne(salaryPlan.getId());
            if (ret.isSystemTemplate()) {
                throw new Ihr360Exception("", "没权限操作！");
            }
        } else {
            if (salaryPlan.isSystemTemplate()) {
                throw new Ihr360Exception("", "没权限操作！");
            }
        }
        validPayrollConfigAndAttendancePlanRelation(salaryPlan);
        salaryPlan = setSalaryPlan(ret, salaryPlan);
        return super.createOrUpdate(salaryPlan.getId(), salaryPlan);
    }

    /**
     * 验证薪资表，考勤方案是否一致
     */
    private void validPayrollConfigAndAttendancePlanRelation(SalaryPlan salaryPlan) {
        AttendancePlan attendancePlan = attendancePlanService.getOne(salaryPlan.getAttendancePlan().getId());
        if (!attendancePlan.getPayrollConfig().getId().equals(salaryPlan.getPayrollConfig().getId())) {
            throw new Ihr360Exception("", "请确认薪资方案和考勤计薪资规则引用的是同一个薪资表.");
        }
    }

    @Override
    public SalaryPlan saveSystemTemplate(SalaryPlan salaryPlan) {
        SalaryPlan ret = new SalaryPlan();
        if (salaryPlan.getId() != null) {
            ret = salaryPlanRepository.findOne(salaryPlan.getId());
            if (!ret.isSystemTemplate()) {
                throw new Ihr360Exception("", "没权限操作！");
            }
        } else {
            if (!salaryPlan.isSystemTemplate()) {
                throw new Ihr360Exception("", "没权限操作！");
            }
        }
        salaryPlan = setSalaryPlan(ret, salaryPlan);
        return salaryPlanRepository.save(salaryPlan);
    }

    private SalaryPlan setSalaryPlan(SalaryPlan ret, SalaryPlan salaryPlan) {
        ret.setName(salaryPlan.getName());
        ret.setMonthCycle(salaryPlan.isMonthCycle());
        ret.setCalculateCycleDay(salaryPlan.getCalculateCycleDay());
        ret.setCalculateCycleMonth(salaryPlan.getCalculateCycleMonth());
        ret.setAttendanceCycleDay(salaryPlan.getAttendanceCycleDay());
        ret.setAttendanceCycleMonth(salaryPlan.getAttendanceCycleMonth());
        ret.setPayrollConfig(salaryPlan.getPayrollConfig());
        ret.setAttendancePlan(salaryPlan.getAttendancePlan());
        ret.setSalaryMonth(salaryPlan.getSalaryMonth());
        if (salaryPlan.getMergerTaxPlan() == null || salaryPlan.getMergerTaxPlan().getId() == null) {
            ret.setMergerTaxPlan(null);
        } else {
            ret.setMergerTaxPlan(salaryPlan.getMergerTaxPlan());
        }
        ret.setReferenceBenefitData(salaryPlan.isReferenceBenefitData());
        ret.setRemark(salaryPlan.getRemark());
        return ret;
    }

    @Override
    public SalaryPlan getOneSystemTemplate(Long id) {
        SalaryPlan salaryPlan = salaryPlanRepository.findOne(id);
        if (salaryPlan == null) {
            return null;
        }
        if (!salaryPlan.isSystemTemplate()) {
            throw new Ihr360Exception("", "您没有权限操作.");
        }
        return salaryPlan;
    }

    @Override
    public SalaryPlan getOneContainSystemTemplate(Long id) {
        SalaryPlan salaryPlan = salaryPlanRepository.findOne(id);
        if (salaryPlan.isSystemTemplate()) {
            return salaryPlan;
        }
        return super.getOne(id);
    }

    @Override
    public List<SalaryPlan> getAllInCompanyContainSystemTemplate() {
        List<SalaryPlan> result = new ArrayList<>();
        result.addAll(salaryPlanRepository.findByIsSystemTemplateAndEnabled(true, true));
        List<SalaryPlan> salaryPlanList = (List<SalaryPlan>) super.getAllInCompany();
        List<SalaryPlan> companyAllSalaryPlans = new ArrayList<>();
        if (!CollectionUtils.isEmpty(salaryPlanList)) {
            salaryPlanList.forEach(salaryPlan -> {
                if (!salaryPlan.isSystemTemplate()) {
                    companyAllSalaryPlans.add(salaryPlan);
                }
            });
        }
        result.addAll(companyAllSalaryPlans);
        return result;
    }

    @Override
    public Page<SalaryPlan> searchSystemTemplate(Ihr360SearchBody<SalaryPlan> searchBody) {
        Specification<SalaryPlan> specification = (root, cq, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressionList = predicate.getExpressions();
            expressionList.add(cb.equal(root.get("isSystemTemplate").as(boolean.class), true));
            if (searchBody.getSpecification() != null) {
                Predicate searchPredicate = searchBody.getSpecification().toPredicate(root, cq, cb);
                if (searchPredicate != null) {
                    return cb.and(predicate, searchBody.getSpecification().toPredicate(root, cq, cb));
                }
            }
            return predicate;
        };
        return salaryPlanRepository.findAll(specification, searchBody.getPageable());
    }

    @Override
    public boolean isDuplicateName(String name, Long id) {
        String companyId = Ihr360SessionContextHolder.getCompanyId();
        return (id != null && id > 0) ?
                salaryPlanRepository.countByCompanyIdAndIdNotAndName(companyId, id, name) > 0 :
                salaryPlanRepository.countByCompanyIdAndName(companyId, name) > 0;
    }

    @Override
    public boolean isDuplicateNameSystemTemplate(String name, Long id) {
        return (id != null && id > 0) ?
                salaryPlanRepository.countByIsSystemTemplateAndIdNotAndName(true, id, name) > 0 :
                salaryPlanRepository.countByIsSystemTemplateAndName(true, name) > 0;
    }

    @Override
    public SalaryPlan createDefaultPayslipConfig(Long id) {
        SalaryPlan salaryPlan = super.getOne(id);
        if (salaryPlan == null) {
            throw new Ihr360Exception("", "没有获取到对应的薪资方案");
        }
        PayslipConfig payslipConfig = new PayslipConfig();
        PayrollConfig payrollConfig = payrollConfigService.getOne(salaryPlan.getPayrollConfig().getId());
        List<SalaryField> salaryFieldList = new ArrayList<>();
        payrollConfig.getFields().forEach(salaryField -> {
            SalaryField field = new SalaryField();
            field.setId(salaryField.getId());
            salaryFieldList.add(field);
        });
        payslipConfig.setFields(salaryFieldList);
        payslipConfig.setDefaultPayslipConfig(true);
        // 暂时解决方案
        payslipConfig.setName(salaryPlan.getName() + "-默认工资单");
        payslipConfig.setCreatedAt(new Date());
        payslipConfigRepository.save(payslipConfig);
        List<PayslipConfig> payslipConfigs = new ArrayList<>();
        payslipConfigs.add(payslipConfig);
        salaryPlan.setPayslipConfigs(payslipConfigs);
        return salaryPlanRepository.save(salaryPlan);
    }

    @Override
    public SalaryPlan cloneSalaryPlan(Long id) {
        String companyId = Ihr360SessionContextHolder.getCompanyId();
        SalaryPlan clonedSalaryPlan = getOneContainSystemTemplate(id);
        if (clonedSalaryPlan == null) {
            throw new Ihr360Exception("", "没有获取到方案");
        }

        // 1.封装新的方案
        SalaryPlan salaryPlan = new SalaryPlan();
        BeanUtils.copyProperties(clonedSalaryPlan, salaryPlan);
        salaryPlan.setCompanyId(companyId);
        salaryPlan.setId(null);
        salaryPlan.setSystemTemplate(false);
        salaryPlan.setPayslipConfigs(new ArrayList<>());
        salaryPlan.setStaffs(new ArrayList<>());

        // 2.clone 上游对象
        AttendancePlan clonedAttendancePlan = clonedSalaryPlan.getAttendancePlan();
        if (clonedAttendancePlan != null) {
            // clone 考勤计薪资规则
            AttendancePlan attendancePlan = attendancePlanService.cloneAttendancePlan(clonedAttendancePlan.getId());
            salaryPlan.setAttendancePlan(attendancePlan);
            salaryPlan.setPayrollConfig(attendancePlan.getPayrollConfig());
        } else {
            // clone 工资表
            PayrollConfig clonedPayrollConfig = clonedSalaryPlan.getPayrollConfig();
            if (clonedPayrollConfig != null) {
                PayrollConfig payrollConfig = payrollConfigService.clonePayrollConfig(clonedPayrollConfig.getId());
                salaryPlan.setPayrollConfig(payrollConfig);
            }
        }

        if (clonedSalaryPlan.getMergerTaxPlan() != null) {
            SalaryPlan mergerTaxPlan = cloneSalaryPlan(clonedSalaryPlan.getMergerTaxPlan().getId());
            salaryPlan.setMergerTaxPlan(mergerTaxPlan);
        }

        // 3.生成方案名称
        String name;
        while (true) {
            // 获取不重复名称
            name = SalaryItemSystemNameUtils.getSystemName(salaryPlan.getName());
            if (!isDuplicateName(name, null)) {
                break;
            }
        }
        salaryPlan.setName(name);

        // 4.保存方案
        return super.createOrUpdate(null, salaryPlan);
    }

    @Override
    public List<SalaryPlan> getCompanySalaryPlan(List<Long> salaryPlanIdList) {
        return salaryPlanRepository.findByIdIn(salaryPlanIdList);
    }

    @Override
    public List<Long> getSystemTemplateIdList() {
        List<SalaryPlan> salaryPlanList = salaryPlanRepository.findByIsSystemTemplate(true);
        return salaryPlanList.stream().map(SalaryPlan::getId).collect(Collectors.toList());
    }
}
